dojo.provide("xray.MainChart");

dojo.require("dojox.charting.Chart2D");
dojo.require("dojox.charting.themes.ET.greys");

dojo.require("dojox.lang.functional");
dojo.require("dojox.lang.functional.fold");

dojo.declare(
	"xray.MainChart",
	null,
	{
		chartInterval:null,
		
		constructor:function(data){
			this.data = data;
			this.MEM_SERIES = 0,
			this.CPU_SERIES = 1;
			this.PAGE_EVENT_SERIES = 2;
			//this.NETWORK_TRAFFIC_SERIES = 2;
			this.LIVE_DATA = true;
			// Store the older timestamp in this.lastTimestampInSeries, this is
			// where we start to get the data for the graph. Take care, this
			// sometimes returns a timestamp older than the last data you had
			// received by xray.Data.get(), this is because the data come in
			// asynchronously, handle this properly!
			//dojo.subscribe("xray/data/new", this, dojo.hitch(this, function(t){ this.lastTimestampInSeries=t<this.lastTimestampInSeries ? t : this.lastTimestampInSeries }));
			
			dojo.subscribe("xray/tools/transport/pause", this, "pause"); // "transport" sounds like a strange name ....
			dojo.subscribe("xray/tools/transport/resume", this, "resume");
		},
		
		create:function(){
			// It stores the drawn series for the next draw, we only have to add
			// the missing pieces.
			// NOTE: The series is stored in reverse!
			this.series = s = {};
			// Initialize all serieses with zeros, so the chart starts drawing on the right.
			var initialData = new Array(xray.config.mainChart.width);
			for (var i=0; i<xray.config.mainChart.width; i++) initialData[i] = 0; // Whats the most efficient to fill an array with zeros?
			var clients = xray.config.clients;
			for (var i=0, l=clients.length; i<l; i++){
				s[clients[i]] = [initialData, initialData, initialData]; // MEM_SERIES, CPU_SERIES, PAGE_EVENT_SERIES
			}
			var c = this.chart = new dojox.charting.Chart2D("mainGraphContent");
			c.setTheme(dojox.charting.themes.ET.greys);
			
			c.addAxis("x", {fixLower:"minor", natural:true, min:1, max:xray.config.mainChart.width, majorTickStep: 10, minorTickStep: 1});
			c.addAxis("y", {vertical: true, min: 0, max: 500, majorTickStep: 50, minorTickStep: 10});
			c.addPlot("default", {type: "Lines"});
			
			c.addPlot("grid", {type: "Grid", hMinorLines: true});

			c.addAxis("y2", {vertical: true, min: 0, max: 100, majorTickStep: 10, minorTickStep: 1, leftBottom: false});
			c.addPlot("cpuPlot", {type: "Lines", hAxis: "x", vAxis: "y2"});
			
			// The event lines
			c.addAxis("yPageEvent", {vertical: true, fixLower: "minor", fixUpper: "major", natural: true, min:0, max:1});
			c.addPlot("pageEventPlot", {type: "Columns", hAxis:"x", vAxis:"yPageEvent"});
			
			var colors = xray.config.colors.chart.series;
			for (var i=0, l=clients.length, client; i<l; i++){
				client = clients[i];
				c.addSeries("memory-"+client, s[client][this.MEM_SERIES], {stroke:{width:2, color:colors[client].memory}});
				c.addSeries("cpu-"+client, s[client][this.CPU_SERIES], {stroke:{width:1, color:colors[client].cpu}, plot: "cpuPlot"});
				//c.addSeries("pageEvent-"+client, s[client][this.PAGE_EVENT_SERIES], {stroke:{width:0}, fill:colors[client].pageEvent, plot: "pageEventPlot"});
			}
			
			c.render();
			this.resume(); // Start the chart drawing interval.
		},
		

		// lastTimestampInSeries: Object
		// 		Stores the last timestamp that was sent to be drawn.
		// 		E.g.  {webkit:123456789, firefox:1235343543}
		lastTimestampInSeries:{},
		draw:function(){
			var now = new Date().getTime(), d;
			for (var i=0, clients = xray.config.clients, l=clients.length, client; i<l; i++){
				client = clients[i];
				if (typeof this.lastTimestampInSeries[client]=="undefined"){
					d = this.data.getByClient(client, now - xray.config.mainChart.width * xray.config.mainChart.resolution);
					// On startup we might not have any data.
//					if (d.length==0 && !d[0]){
////console.log('NO DATA for = ', client);
//						continue;
//					}
////??????is this really the LAST timestamp? is it not the first one of the last drawn ones?
//					this.lastTimestampInSeries[client] = d[0].timestamp;
				}else{
					d = this.data.getByClient(client, this.lastTimestampInSeries[client]);
				}
				if (d.length==0 && !d[0]){
console.log('NO DATA for = ', client);
					continue;
				}
//console.log('client, d = ', client, dojo.toJson(d, true));
//console.log(client, dojo.toJson(dojo.map(d, function(item){return item.memory}), true))
				this.updateSeries(client, d, now);
			}
			
			// Adjust the y axis, which shows the memory consumption.
			//var max = 0;
			//for (var i=0; i<s[MEM_SERIES].length; i++){
			//    console.log("max=", 123);
			//	max = max>s[MEM_SERIES][i] ? max : s[MEM_SERIES][i];
			//}
			//console.log("max=", max);
			//
			//var pow = Math.pow(10, String(max).length-1);
			//max = Math.ceil(max / pow) * pow;
			//console.log("max=", max);
			//chart.axes.y.destroy();
			//var kwargs = {vertical: true, min:0, max:parseInt(max), majorTickStep: 50, minorTickStep: 10};
			//chart.addAxis("y", {vertical: true, min:0, max:parseInt(max), majorTickStep: 50, minorTickStep: 10});
			//var xs = new dojox.charting.axis2d.Default(chart, kwargs);
			//xs.name = "y"; xs.dirty=true;
			//chart.dirty = true;
			//chart.axes.y = xs;
			this.chart.render();
		},
		
		updateSeries:function(client, data, nowTimestamp){
			var d = data,
				df = dojox.lang.functional,
				s = this.series,
				now = nowTimestamp;
			// Prepare the data for the series.
			// Get the data and build an index by timestamp.
			var timestampToDataMap = {};
			for (var i=0, l=d.length; i<l; i++){
				timestampToDataMap[d[i].timestamp] = d[i];
			}
			
			var lastData = d[d.length-1];
			// !!! TODO: Depending on data format we can not expect the last timestamp to be now.
			// When loading old debugging sessions the difference between the last and current timestamp 
			// creates looks with hundreds of thousands of iterations.
			// Need to be able to distinct between live and archive mode!!!!!!!
			// Following code is a HACK and doesn't address the fact that the get() method of the data provider not always 
			// has to get called with a timestamp.. see the this.data.get(now) call above
			if (!this.LIVE_DATA){
				var now = lastData.timestamp;
			}
			
			// The timestamp when the data were last pushed into the series.
			var lastTimestamp = now;
			// Collect the data in dataPoint until we can push them into a series' data point, which will
			// only get the average of the collected data.
			dataPoints = [[], [], 0]; // mem, cpu, page events
			// Fill the array form the end, until we reached xray.config.mainChart.width.
			var newData;
			
//console.log('ms, now, this.lastTimestampInSeries[client] = ', ms, now, this.lastTimestampInSeries[client]);
			for (var ms=now; ms > this.lastTimestampInSeries[client]; ms--){
				// Update the lastData, if there are data in this.data for the
				// current timestamp, this makes sure that always the newest,
				// latest value is stored in lastData, relative to ms!
				// Which means if ms=10 the data for the closest timestamp that is SMALLER
				// are contained in lastData.
				if (timestampToDataMap[ms]){
					newData = timestampToDataMap[ms];
					if (!newData.memory) newData.memory = lastData.memory;
					if (!newData.cpu) newData.cpu = lastData.cpu;
					lastData = newData;
					// publish data change
// FIXXXME this must go into xray.Data!!!! and this is not performing well i guess (wolfram) imho we should remove all pubs from this "core"
					dojo.publish("/xray/memChange", [lastData.memory]);
					dojo.publish("/xray/cpuChange", [lastData.cpu]);
				}
				if (lastTimestamp - ms < xray.config.mainChart.resolution){
					if (ms==lastData.timestamp){
						if (lastData.memory) dataPoints[this.MEM_SERIES].push(+lastData.memory);
						if (lastData.cpu) dataPoints[this.CPU_SERIES].push(+lastData.cpu);
						if (lastData.log.category=="page") dataPoints[this.PAGE_EVENT_SERIES] = 1;
					}
				}else{
					var dp = dataPoints[this.MEM_SERIES].length ? dataPoints[this.MEM_SERIES] : [lastData.memory];
//console.log(2, dojo.toJson(dp, true));
					// Make it mega bytes (divide by 1024).
					s[client][this.MEM_SERIES].push((df.reduce(dp, function(a, b){return a+b}) / dp.length)/1024);
					dp = dataPoints[this.CPU_SERIES].length ? dataPoints[this.CPU_SERIES] : [lastData.cpu];
					s[client][this.CPU_SERIES].push(df.reduce(dp, function(a, b){return a+b}) / dp.length);
					s[client][this.PAGE_EVENT_SERIES].push(dataPoints[this.PAGE_EVENT_SERIES]);
					dataPoints = [[], [], 0];
					lastTimestamp = ms;
				}
			}
			this.lastTimestampInSeries[client] = now;
			// If we have someting stored dataPoints push it out to the graph.
			if (dataPoints[this.MEM_SERIES].length){
//console.log('dataPoints = ', dojo.toJson(dataPoints, true));
				var dp = dataPoints[this.MEM_SERIES];
				s[client][this.MEM_SERIES].push((df.reduce(dp, function(a, b){return a+b}) / dp.length)/1024);
//console.log('s[client][this.MEM_SERIES] = ', s[client][this.MEM_SERIES][s[client][this.MEM_SERIES].length-1]);
				dp = dataPoints[this.CPU_SERIES];
				s[client][this.CPU_SERIES].push(df.reduce(dp, function(a, b){return a+b}) / dp.length);
				s[client][this.PAGE_EVENT_SERIES].push(dataPoints[this.PAGE_EVENT_SERIES]);
			}
			// Limit the number of dataPoints to xray.config.mainChart.width
			if (s[client][this.MEM_SERIES].length > xray.config.mainChart.width){
				var sliceAt = s[client][this.MEM_SERIES].length - xray.config.mainChart.width;
				s[client][this.MEM_SERIES] = s[client][this.MEM_SERIES].slice(sliceAt);
				s[client][this.CPU_SERIES] = s[client][this.CPU_SERIES].slice(sliceAt);
				s[client][this.PAGE_EVENT_SERIES] = s[client][this.PAGE_EVENT_SERIES].slice(sliceAt);
			}
			// Draw the series.
			this.chart.updateSeries("memory-"+client, s[client][this.MEM_SERIES]);
//var cccc = s[client][this.MEM_SERIES];
//console.log('s[client][this.MEM_SERIES] = ', client, cccc.length, this.MEM_SERIES, cccc[0], cccc[1], dojo.toJson(s[client][this.MEM_SERIES], true));
			this.chart.updateSeries("cpu-"+client, s[client][this.CPU_SERIES]);
			this.chart.updateSeries("pageEvent-"+client, s[client][this.PAGE_EVENT_SERIES]);
		},
		
		resize: function(width, height){
			
		},
		
		pause: function(){
			if (this.chartInterval) {
				clearInterval(this.chartInterval);
				this.chartInterval = null;
			}
		},
		
		resume: function(){
			if (!this.chartInterval){
				this.chartInterval = setInterval(dojo.hitch(this, "draw"), xray.config.mainChart.drawInterval);
			}
		}
	}
);
